package basic;


import com.google.common.base.Stopwatch;
import executor.ExecutorFactory;
import io.AofReader;
import io.AofWriter;
import protocol.Request;
import protocol.RespParser;
import protocol.Response;
import server.JodisConfig;
import utils.Logger;
import utils.StringUtils;

import javax.naming.ConfigurationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * DB对象，有保存对象和过期时间key
 * 从磁盘加载之前的数据，AOF加载
 * **/
public class JodisDb {

    private Logger logger = Logger.getLogger(JodisDb.class);

    /**
     * 存储不带过期时间的key
     */
    private ConcurrentHashMap<String, JodisObject> jodisCollection;

    /**
     * 存储带过期时间的key
     */
    private ConcurrentHashMap<String, Long> expireJodisCollection;

    private ExecutorFactory executorFactory;

    private AofWriter aofWriter;

    private AofReader aofReader;

    private RespParser respParser;

    private JodisConfig jodisConfig;
    /**
     * 请求队列
     */
    private List<Request> requestQueue;

    private boolean noNeed;

    /**
     * 是否正在进行Wal文件重写
     */
    private AtomicBoolean rewriteWal;

    /**
     * default for test
     */
    public JodisDb(JodisConfig jodisConfig) throws ConfigurationException, IOException {
        jodisCollection = new ConcurrentHashMap<>();
        expireJodisCollection = new ConcurrentHashMap<>();
        executorFactory = new ExecutorFactory(this);
        aofWriter = new AofWriter(jodisConfig.getLogDir(), jodisConfig.getLogWal());
        aofReader = new AofReader(jodisConfig.getLogDir(), jodisConfig.getLogWal());
        //对客户端发送过来的字符串进行解析
        respParser = new RespParser();
        requestQueue = new ArrayList<>(10);
        rewriteWal = new AtomicBoolean(false);
        //默认是保存aof
        noNeed = false;
        this.jodisConfig = jodisConfig;
        //从磁盘加载数据，调用execute()方法重新加载到哈希表中即可
        loadFromLog();
    }

    public JodisObject put(String key, JodisObject value) {
        return jodisCollection.put(key, value);
    }

    public JodisObject get(String key) {
        return jodisCollection.get(key);
    }

    public int expire(String key, long timestamp){
        expireJodisCollection.put(key,timestamp);
        return 1;
    }

    /**
     * -1表示未设置过期时间，否则返回到期时间戳
     * **/
    public long getExpireByKey(String key){
        if(expireJodisCollection.containsKey(key)){
            return expireJodisCollection.get(key);
        }
        return -1;
    }

    /**
     * @return
     */
    public int size() {
        return jodisCollection.size();
    }

    /**
     * @param key
     */
    public void remove(String key) {
        this.jodisCollection.remove(key);
    }

    public void removeExpire(String key) {
        this.expireJodisCollection.remove(key);
    }

    /**
     * clear
     */
    public void clear() {
        this.jodisCollection.clear();
    }

    /**
     * @param key
     * @return
     */
    public boolean containsKey(String key) {
        return this.jodisCollection.containsKey(key);
    }

    /**
     * @param key
     * @return
     */
    public boolean containsExpireKey(String key) {
        return this.expireJodisCollection.containsKey(key);
    }

    /**
     * @return
     */
    public Set<String> keySet() {
        return this.jodisCollection.keySet();
    }

    /**
     * 执行命令
     * 数据持久化
     * @param request
     * @return
     */
    public Response execute(Request request) throws IOException {
        Response response = executorFactory.execute(request);
        // 检测是否需要进行Wal
        if (!noNeed && request.needLog() && !response.isError()) {
            // 如果正在进行aof重写，利用原子类来判断逻辑
            if (rewriteWal.get()) {
                requestQueue.add(request);
            } else {
                aofWriter.write(request.getRequest());
            }
        }
        return response;
    }

    /**
     * 执行
     * @param request
     * @return
     * @throws IOException
     */
    public Response execute(String request) throws IOException {
        Request req = respParser.parse(request);
        return execute(req);
    }

    public Response execute(String request, boolean isNeed) throws IOException {
        Request req = respParser.parse(request);
        req.needLog(isNeed);
        return execute(req);
    }

    /**
     * 从磁盘加载数据
     */
    public void loadFromLog() throws IOException {
        int loadMode = jodisConfig.getReloadMode();
        if(loadMode == JodisConstant.AOF_MODE){
            logger.info("Load from disk with WAL mode started");
            Stopwatch stopwatch = Stopwatch.createStarted();
            loadFromAof();
            logger.info("Load from disk with WAL mode finished, cost: {}", stopwatch.stop().toString());
        }
    }

    //加载AOF日志
    private void loadFromAof() throws IOException {
        Iterator<String> iterator = aofReader.readAof();
        while (iterator.hasNext()){
            String request = iterator.next();
            if (StringUtils.isNotEmpty(request)) {
                //这个不用保存到AOF中
                execute(request.trim(), false);
            }
        }
    }
}
